: not 0= ;
: even? dup 2 mod 0= ;

: input-filename s" input" ;

256 Constant max-bytes-line
Create line-buffer max-bytes-line 2 + allot
input-filename r/o open-file throw Value puzzle-input-handle

\ https://rosettacode.org/wiki/Substring#Forth :
\ 2 constant Pos
\ 3 constant Len
\ : Str ( -- c-addr u )  s" abcdefgh" ;

\ Str Pos /string drop Len type    \ cde
\ Str Pos /string type             \ cdefgh
\ Str 1- type                      \ abcdefg
\ Str char d scan drop Len type    \ def
\ Str s" de" search 2drop Len type \ def

: split-in-half ( c-addr length -- c-addr1 length1 c-addr2 length2 )
  2dup  \ c-addr length  c-addr length
  dup 2 /  \ c-addr length  c-addr length  length-half
  /string  \ c-addr length  c-addr2 length2
  2swap  \ c-addr2 length2  c-addr length
  2 /  \ c-addr2 length2  c-addr1 length1
  2swap ;  \ c-addr2 length2  c-addr1 length1

\ https://rosettacode.org/wiki/Character_codes#Forth :
\ char a
\ dup .    \ 97
\ emit     \ a

: upper-case? ( char -- flag )
  dup dup
  65 >= swap 90 <= and ;


\ https://gforth.org/manual/Memory-Access.html#index-c_0040_0028--c_002daddr-_002d_002d-c--_0029-core :
\ c@ ( c-addr – c ) core “c-fetch”
\ c is the char stored at c_addr.

: char-priority ( char -- score )
  upper-case? if
    38 -  \ upper-case letter shift to 27 - 52 is 38
  else
    96 -  \ lower-case letter shift to 1 - 26 is 96
  then ;

\ tests
char a char-priority 1 = 0= throw
char z char-priority 26 = 0= throw
char A char-priority 27 = 0= throw
char Z char-priority 52 = 0= throw
s" after tests:" type .s cr

: common-char ( c-addr1 length1 c-addr2 length2 -- char )
  2swap  \ c-addr2 length2 c-addr1 length1
  begin
    dup 0> while
    \ put length on return stack
    >r  \ c-addr2 length2 c-addr1
    dup c@  \ c-addr2 length2 c-addr1 char
    swap  \ c-addr2 length2 char c-addr1
    >r  \ put c-addr1 on return stack
    >r  \ put character on return stack
    \ c-addr2 length2
    2dup r>  \ c-addr2 length2  c-addr2 length2  char
    \ search for the character in the second string
    scan 0<> if  \ c-addr2 length2 c-addr2'
      c@ -rot
      \ discard the string, we have found the common character
      2drop
      \ cleanup return stack
      \ c-addr1
      rdrop
      \ length
      rdrop
      -1  \ put a true on the stack to signal success
      exit
    else
      drop  \ drop the empty string char address
      r>  \ c-addr1
      r>  \ length
      \ c-addr2 length2 c-addr1 length1
      \ drop first character from string
      1 /string
    then
  repeat ;

\ tests
s" RNCBcwmHr" s" tdltdtmhl"
common-char drop char m = 0= throw
s" HRzCCRZNBRrRwMNwHH" s" WDzDPnvvGnsWLWpGJJ"
common-char drop char z = 0= throw
s" ddgShFQhjljqhggbdbbt" s" DsDsQnJDnWsJnJvrQDPJ"
common-char drop char Q = 0= throw
s" DSRHPhHmRSnndHnHRM" s" LqvbLLTdvlfdqTLtbv"
common-char drop char d = 0= throw
\ s" after tests:" type .s cr

: process-line ( c-addr length -- score )
  split-in-half  \ c-addr1 length1 c-addr2 length2
  \ s" after splitting line in half:" type .s cr
  common-char if
    char-priority
  else
    s" invalid input, no common character" type bye
  then ;

: process ( c-addr length -- ??? )
  0  \ accumulator value for score
  begin puzzle-input-handle file-eof? not
  while
    \ s" at beginning of loop:" type .s cr

    \ read lines until the file has been completely processed
    line-buffer max-bytes-line puzzle-input-handle read-line throw  \ stack: acc length eof-flag
    \ check that everything was read OK from the line buffer
    swap dup 0 > rot and
    if
      line-buffer swap  \ acc c-addr length
      \ Check, that each line has an even number of characters.
      even? if
        \ Remove trailing whitespace.
        -trailing
        \ Calculate for each line.
        process-line
        \ accumulate the result, summing the priorities
        s" result of processing line:" type dup . cr
        +
        s" accumulated:" type .s cr
      else
        s" invalid input" type cr bye
      then
    then
  repeat
  \ drop length of read characters from EOF read
  drop ;

process .
puzzle-input-handle close-file
